//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.eResearch.WFServiceLibrary.SchedulerLogic
{
    using System;
    using System.Runtime.InteropServices;
    using V1 = TaskScheduler.V1;
    using V2 = TaskScheduler;

    /// <summary>
    /// Provides access to the Task Scheduler service for managing registered tasks.
    /// </summary>
    public sealed class TaskService : IDisposable
    {
        internal static readonly bool hasV2 = (Environment.OSVersion.Version >= new Version(6, 0));
        internal V1.ITaskScheduler v1TaskScheduler = null;
        private WindowsImpersonatedIdentity v1Impersonation = null;
        internal V2.TaskSchedulerClass taskScheduler;
        internal bool v2 = false;

        /// <summary>
        /// Creates a new instance of a TaskService connecting to the local machine as the current user.
        /// </summary>
        public TaskService()
            : this(null)
        {
        }

        /// <summary>
        /// Creates a new instance of a TaskService connecting to a remote machine as the current user.
        /// </summary>
        /// <param name="targetServer">The target server.</param>
        public TaskService(string targetServer)
            : this(targetServer, null, null, null)
        {
        }

        /// <summary>
        /// Creates a new instance of a TaskService connecting to a remote machine as the specified user.
        /// </summary>
        /// <param name="targetServer">The target server.</param>
        /// <param name="userName">Name of the user.</param>
        /// <param name="accountDomain">The account domain.</param>
        /// <param name="password">The password.</param>
        public TaskService(string targetServer, string userName, string accountDomain, string password) :
            this(targetServer, userName, accountDomain, password, false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskService"/> class.
        /// </summary>
        /// <param name="targetServer">The target server. A null value implies the local machine.</param>
        /// <param name="userName">Name of the user.</param>
        /// <param name="accountDomain">The account domain.</param>
        /// <param name="password">The password.</param>
        /// <param name="forceV1">If set to <c>true</c> force Task Scheduler 1.0 compatibility.</param>
        public TaskService(string targetServer, string userName, string accountDomain, string password, bool forceV1)
        {
            if (hasV2 && !forceV1)
            {
                this.v2 = true;

                this.taskScheduler = new V2.TaskSchedulerClass();
                // Check to ensure character only server name. (Suggested by bigsan)
                if (!string.IsNullOrEmpty(targetServer) && targetServer.StartsWith(@"\"))
                    targetServer = targetServer.TrimStart('\\');
                this.taskScheduler.Connect(targetServer, userName, accountDomain, password);
            }
            else
            {
                this.v1Impersonation = new WindowsImpersonatedIdentity(userName, accountDomain, password);
                V1.CTaskScheduler csched = new V1.CTaskScheduler();
                this.v1TaskScheduler = (V1.ITaskScheduler)csched;
                if (!string.IsNullOrEmpty(targetServer))
                {
                    // Check to ensure UNC format for server name. (Suggested by bigsan)
                    if (!targetServer.StartsWith(@"\\"))
                        targetServer = @"\\" + targetServer;
                    this.v1TaskScheduler.SetTargetComputer(targetServer);
                }
            }
        }

        /// <summary>
        /// Returns an empty task definition object to be filled in with settings and properties and then registered using the <see cref="TaskFolder.RegisterTaskDefinition(string, TaskDefinition)"/> method.
        /// </summary>
        /// <returns><see cref="TaskDefinition"/> instance for setting properties.</returns>
        public TaskDefinition NewTask()
        {
            if (this.v2)
                return new TaskDefinition(this.taskScheduler.NewTask(0));
            Guid ITaskGuid = Marshal.GenerateGuidForType(typeof(V1.ITask));
            Guid CTaskGuid = Marshal.GenerateGuidForType(typeof(V1.CTask));
            string v1Name = "Temp" + Guid.NewGuid().ToString("B");
            return new TaskDefinition(this.v1TaskScheduler.NewWorkItem(v1Name, ref CTaskGuid, ref ITaskGuid), v1Name);

        }

        /// <summary>
        /// Gets the root ("\") folder. For Task Scheduler 1.0, this is the only folder.
        /// </summary>
        public TaskFolder RootFolder
        {
            get { return this.GetFolder(@"\"); }
        }

        /// <summary>
        /// Gets the path to a folder of registered tasks.
        /// </summary>
        /// <param name="folderName">The path to the folder to retrieve. Do not use a backslash following the last folder name in the path. The root task folder is specified with a backslash (\). An example of a task folder path, under the root task folder, is \MyTaskFolder. The '.' character cannot be used to specify the current task folder and the '..' characters cannot be used to specify the parent task folder in the path.</param>
        /// <returns><see cref="TaskFolder"/> instance for the requested folder.</returns>
        public TaskFolder GetFolder(string folderName)
        {
            return this.v2 ? new TaskFolder(this.taskScheduler.GetFolder(folderName)) : new TaskFolder(v1TaskScheduler);
        }

        /// <summary>
        /// Gets the task.
        /// </summary>
        /// <param name="iSvc">The i SVC.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        internal static V2.IRegisteredTask GetTask(V2.ITaskService iSvc, string name)
        {
            V2.ITaskFolder fld = null;
            try
            {
                fld = iSvc.GetFolder("\\");
                return fld.GetTask(name);
            }
            finally
            {
                if (fld != null) Marshal.ReleaseComObject(fld);
            }
        }

        /// <summary>
        /// Gets the task.
        /// </summary>
        /// <param name="iSvc">The i SVC.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        internal static V1.ITask GetTask(V1.ITaskScheduler iSvc, string name)
        {
            Guid ITaskGuid = Marshal.GenerateGuidForType(typeof(V1.ITask));
            return iSvc.Activate(name, ref ITaskGuid);
        }

        /// <summary>
        /// Gets the task with the specified path.
        /// </summary>
        /// <param name="taskPath">The task path.</param>
        /// <returns>The task.</returns>
        public Task GetTask(string taskPath)
        {
            Task t = null;
            if (this.v2)
            {
                V2.IRegisteredTask iTask = GetTask(this.taskScheduler, taskPath);
                if (iTask != null)
                    t = new Task(iTask);
            }
            else
            {
                V1.ITask iTask = GetTask(this.v1TaskScheduler, taskPath);
                if (iTask != null)
                    t = new Task(iTask);
            }
            return t;
        }

        /// <summary>
        /// Gets the highest version of Task Scheduler that a computer supports.
        /// </summary>
        public Version HighestSupportedVersion
        {
            get
            {
                if (this.v2)
                {
                    uint v = this.taskScheduler.HighestVersion;
                    return new Version((int)(v >> 16), (int)(v & 0x0000FFFF));
                }
                return new Version(1, 1);
            }
        }

        /// <summary>
        /// Releases all resources used by this class.
        /// </summary>
        public void Dispose()
        {
            if (this.taskScheduler != null)
                Marshal.ReleaseComObject(this.taskScheduler);
            if (this.v1TaskScheduler != null)
                Marshal.ReleaseComObject(this.v1TaskScheduler);
            if (this.v1Impersonation != null)
                this.v1Impersonation.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}
